#include "uvbotlib.h"

//================== tokenizer.cpp ======================

Tokenizer::Tokenizer(string t)
{
  text = t;
  delims = DEFAULT_DELIMS;
  useQuotes = true;
  parse();
}

Tokenizer::Tokenizer(string t, string d)
{
  text = t;
  delims = d;
  useQuotes = false;
  parse();
}

string Tokenizer::operator[](int index)
{
  return tokens[index];
}

void Tokenizer::parse()
{
  // end MARKS THE END OF THE TOKEN, AND scanEnd MARKS THE
  // PLACE WHERE SCANNING LEFT OFF.  IF QUOTES ARE NOT BEING
  // USED THEN end AND scanEnd SHOULD HAVE THE SAME VALUE.
  // IF QUOTES ARE BEING USED, THE VALUE OF scanEnd SHOULD
  // BE ONE GREATER THAN THE VALUE OF end.
  size_t start, end = 0, scanEnd = 0;

  start = text.find_first_not_of(delims, end);
  if (start == string::npos) return;   // BLD 28NOV07
  if (text[start] != QUOTE || !useQuotes) {
    end = text.find_first_of(delims, start);
    scanEnd = end;
  } else {
    start++;    // MOVE PAST OPENING QUOTE
    end = text.find_first_of(QUOTE_STR, start);
    scanEnd = end + 1;
  }

  while (end != string::npos) {
    tokens.push_back(text.substr(start, end - start));
    start = text.find_first_not_of(delims, scanEnd);
    if (text[start] != QUOTE || !useQuotes) {
      end = text.find_first_of(delims, start);
      scanEnd = end;
    } else {
      start++;    // MOVE PAST OPENING QUOTE
      end = text.find_first_of(QUOTE_STR, start);
      scanEnd = end + 1;
    }
  }

  if (start != string::npos) {
    tokens.push_back(text.substr(start, end - start));
  }
}


//=========== connectionWin.cpp REMOVED ===============

//================== botutil.cpp ======================

string trim(string str)
{
    string whiteSpace = " \t\n\r";
    int start, end;

    start = str.find_first_not_of(whiteSpace);
    end = str.find_last_not_of(whiteSpace) + 1;
    if (start == string::npos) {
	// STRING CONTAINS ONLY WHITE SPACE
	return "";
    }
    return str.substr(start, end - start);
}

// FINDS THE STRING THAT FOLLOWS THE KEY AND ENDS IN
// WHITE SPACE
string lookup(string str, string key)
{
    string whiteSpace = " \t\n\r";
    int start = 0, end = 0;

    start = str.find(key) + key.length();
    end = str.find_first_of(whiteSpace, start);
    if (start == string::npos) {
	return "";
    }
    if (end == string::npos) {
	end = str.length();
    }
    return str.substr(start, end - start);
}

//================== scan.cpp ======================

Scan::Scan()
{
    id = INVALID;
    loc.x = INVALID;
    loc.y = INVALID;
    facing = INVALID;
    type = INVALID;
    pupType = INVALID;
    pupAmount = INVALID;
}

Scan::Scan(string scanStr)
{
  Tokenizer tokens(scanStr, ":");

  if (tokens.size() > 4) {
    type = tokens[0][0];
    id = atoi(tokens[1].c_str());
    loc.x = atoi(tokens[2].c_str());
    loc.y = atoi(tokens[3].c_str());

    if (type == BOT_SCAN) {
      facing = atoi(tokens[4].c_str());
    } else if (type == PUP_SCAN) {
      pupType = atoi(tokens[4].c_str());
      if (tokens.size() > 5) {
	pupAmount = atoi(tokens[5].c_str());
      } else {
	pupAmount = INVALID;
      }
    } else {
      // TYPE IS UNKNOWN
    }
  } else {
    id = INVALID;
    loc.x = INVALID;
    loc.y = INVALID;
    facing = INVALID;
    pupType = INVALID;
    pupAmount = INVALID;
  }
}

//================== botstatus.cpp ======================

BotStatus::BotStatus()
{
    facing = damage = maxDamage = power = maxPower = INVALID;
    loc.x = loc.y = INVALID;
    map = "";
    statusStr = "";
	test = 0;
}

void BotStatus::parse(string statusStrIn)
{
    int start, end;
    string delims = " \t\n\r";
    string str;
    int row, col;
    Tokenizer tokens("");
    int i;
    Scan *scanPtr;

    start = 0, end = 0;
    statusStr = statusStrIn;

    end = statusStr.find("loc:");
    cmdResult = trim(statusStr.substr(0, end));

    str = lookup(statusStr, "loc:");
    tokens = Tokenizer(str, ",");
    loc.x = atoi(tokens[0].c_str());
    loc.y = atoi(tokens[1].c_str());

    str = lookup(statusStr, "fac:");
    facing = atoi(str.c_str());

    str = lookup(statusStr, "dmg:");
    tokens = Tokenizer(str, "/");
    damage = atoi(tokens[0].c_str());
    maxDamage = atoi(tokens[1].c_str());

    str = lookup(statusStr, "pwr:");
    tokens = Tokenizer(str, "/");
    power = atoi(tokens[0].c_str());
    maxPower = atoi(tokens[1].c_str());

    map = trim(lookup(statusStr, "map:"));

    str = trim(lookup(statusStr, "scn:"));
    tokens = Tokenizer(str, ",");
    deleteScans();

    for (i = 0; i < (int)tokens.size(); i++) 
	{
		scanPtr = new Scan(tokens[i]);
		if (scanPtr->getId() != Scan::INVALID) 
		{
			if (scanPtr->getType() == Scan::BOT_SCAN) 
			{
				scanTargets.push_back(scanPtr);
			} 
			else 
			{
				// KEEP SEPARATE LIST OF PUP SCANS
				scanPups.push_back(scanPtr);
			}
		} 
		else 
		{
			delete scanPtr;
		}
    }
}

BotStatus::~BotStatus()
{
    // DELETE IN SEPARATE FUNCTION SO IT CAN BE CALLED
    // FROM parse ALSO.
    deleteScans();
}

void BotStatus::deleteScans()
{
    int i;

    for (i = 0; i < scanTargets.size(); i++) {
	delete scanTargets[i];
    }
    scanTargets.clear();

    for (i = 0; i < scanPups.size(); i++) {
	delete scanPups[i];
    }
    scanPups.clear();
}

Scan BotStatus::getScan(int i)
{
    Scan result;

    if (i < (int)scanTargets.size()) {
	result = *(scanTargets[i]);
    }
    return result;
}

Scan BotStatus::getPupScan(int i)
{
    Scan result;

    if (i < (int)scanPups.size()) {
	result = *(scanPups[i]);
    }
    return result;
}

int BotStatus::getTargetAngle(Scan s)
{
    return getAngle(loc, s.getLoc());
}

float BotStatus::getTargetRange(Scan s)
{
    return getDistance(loc, s.getLoc());
}

float BotStatus::getDistance(Loc l1, Loc l2)
{
    return sqrt(pow((float)l2.x -l1.x, 2) + pow((float)l2.y - l1.y, 2));
}

// ZERO IS STRAIGHT UP AND THE ANGLE INCREASES
// IN A CLOCKWISE DIRECTION

int BotStatus::getAngle(Loc start, Loc end)
{
    int x1, y1, x2, y2;
    int degrees = 0;
    float radians = 0.0;

    x1 = start.x;
    y1 = start.y;
    x2 = end.x;
    y2 = end.y;

    if (x1 != x2 && y1 != y2)
    {
	if (x2 > x1 && y2 > y1) // P/2 TO PI  RIGHT AND DOWN
	{
	    radians = fabs(atan(((float)y2 - y1) / (x2 - x1))) + PI / 2;
	}
	else if (x2 < x1 && y2 > y1) // PI/ TO 3PI/2  LEFT AND DOWN
	{
	    radians = fabs(atan(((float)x2 - x1) / (y2 - y1))) + PI;
	}
	else if (x2 < x1 && y2 < y1) // 3PI/2 TO 2PI  LEFT AND UP
	{
	    radians = fabs(atan(((float)y2 - y1) / (x2 - x1))) + 3 * PI / 2;
	}
	else if (x2 > x1 && y2 < y1) // LESS THAN PI/2  RIGHT AND UP
	{
	    radians = fabs(atan(((float)x2 - x1) / (y2 - y1)));
	}
    }
    else if (x2 == x1 && y2 < y1)   // VERTICAL UP
    {
	radians = 0;
    }
    else if (x2 == x1 && y2 > y1)   // VERTICAL DOWN
    {
	radians = PI;
    }
    else if (y2 == y1 && x2 < x1)   // HORIZONTAL LEFT
    {
	radians = 3 * PI / 2;
    }
    else if (y2 == y1 && x2 > x1)   // HORIZONTAL RIGHT
    {
	radians = PI / 2;
    }
    degrees = (int)((radians * 360) / (2 * PI));
    return degrees;
}

//================== uvbot.cpp ======================

UvBot::UvBot()
{
    id = -1;    // NO ID ASSIGNED YET
}

UvBot::~UvBot()
{
    conn.close();
}

string UvBot::connect()
{
    return connect("localhost", 4999);
}

string UvBot::connect(string hostName, int port, string botName)
{
  string line;
  Tokenizer tokens("");
  string result;

  conn.connect(hostName, port);

  if (botName == "")
  {
    conn.write("bot");
  }
  else
  {
    conn.write("bot " + botName);
  }

  line = conn.read();

  tokens = Tokenizer(line);
  if (tokens[0] == "id") {
      id = atoi(tokens[1].c_str());
      result = "connected to " + hostName + " as id " + tokens[1];
  } else {
      id = -2;    // NO ID RECEIVED FROM SERVER
      result = "could not connect to " + hostName;
  }

  return result;
}

string UvBot::connect(string hostName, int port)
{
  string result;
  result = connect(hostName, port, "");
  return result;
}

string UvBot::doCmd(string cmdStr)
{
    string result;
    char cmdBuff[DFLT_STR_LEN];

    sprintf(cmdBuff, "bot %d %s", id, cmdStr.c_str());
    conn.write(cmdBuff);
    result = conn.read();
    return result;
}


